/**
 * PT.js
 * 一个常用的js工具库
 * 作者：码农107
 * 2020-12-31
 */

;(function(){

    const PublicTool = function(){};

    /** 数据类型检测
     */
    PublicTool.prototype.isNull = function(val){ return val === null }
    PublicTool.prototype.isUndefined = function(val){ return val === undefined }
    PublicTool.prototype.isString = function(val){ return typeof val === 'string' }
    PublicTool.prototype.isNumber = function(val){ return typeof val === 'number' }
    PublicTool.prototype.isArray = function(val){ return Array.isArray(val) }
    PublicTool.prototype.isFunction = function(val){ return typeof val === 'function' }
    PublicTool.prototype.isObject = function(val){ return val.toString() === '[object Object]' }
    PublicTool.prototype.isPromise = function(val){ return val.toString() === '[object Promise]' }

    /** 其他检测类
     */
    //是否整数
    PublicTool.prototype.isIntNumber = function(str){ return (new RegExp(/^[0-9]{1,}$/)).test(str) }
    //是否小数
    PublicTool.prototype.isFloatNumber = function(str, decimal){ 
        const deg = typeof decimal === 'number' ? RegExp(`^[0-9]+[.][0-9]{${decimal}}$`, 'ig') : RegExp(`^[0-9]+[.][0-9]+$`, 'ig')
        return deg.test(str)
    }
    //是否邮箱
    PublicTool.prototype.isEmail = function(str){ return (new RegExp(/^[a-zA-Z0-9]+[@][a-zA-Z0-9]+[.][a-zA-Z0-9]+$/)).test(str) }
    //是否中国大陆身份证号
    PublicTool.prototype.isUserCard = function(str){ return /^[1-9][0-9]{5}(18|19|(2[0-9]))[0-9]{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)[0-9]{3}[0-9Xx]$/.test(str); }
    //是否中文
    PublicTool.prototype.isChine = function(str){ return !(new RegExp(/^([u4E00-u9FA5]|[uFE30-uFFA0])*$/)).test(str) }
    //是否安卓
    PublicTool.prototype.isAndroid = function(){
        var u = navigator.userAgent;
        return u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
    }
    //是否ios
    PublicTool.prototype.isiOS = function(){
        var u = navigator.userAgent;
        return !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
    }
    //是否微信浏览器
    PublicTool.prototype.isWeixin = function(){
        var ua = navigator.userAgent.toLowerCase();
        return ua.match(/MicroMessenger/i) === "micromessenger";
    }
    //是否是qq号
    PublicTool.prototype.isQQ = function(value){ return /^[1-9][0-9]{4,12}$/.test(value.toString()); }

    /** Cookie操作
     * cname: 键
     * cvalue: 值
     * ms: 有效期（毫秒）
     */
    PublicTool.prototype.setCookie = function(cname, cvalue, ms){
        var d = new Date();
        d.setTime(d.getTime()+ ms);
        var expires = "expires="+d.toGMTString();
        document.cookie = cname+"="+cvalue+"; "+expires;
    }
    PublicTool.prototype.getCookie = function(cname){
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for(var i=0; i<ca.length; i++) {
            var c = ca[i].trim();
            if (c.indexOf(name)==0) { return c.substring(name.length,c.length); }
        }
        return undefined;
    }
    PublicTool.prototype.delCookie = function(cname){
        var exp = new Date();
        exp.setTime(exp.getTime() - 1);
        var cval= this.getCookie(cname);
        if(cval!=null) document.cookie= cname + "="+cval+";expires="+exp.toGMTString();
    }

    /** Storage操作
     * sname: 键
     * svalue: 值
     * session: 是否sessionStorage
     */
    PublicTool.prototype.setStorage = function(sname, svalue, session){
        var S = session ? sessionStorage : localStorage;
        var data = {data: svalue}
        S.setItem(sname, JSON.stringify(data));
    }
    PublicTool.prototype.getStorage = function(sname, session){
        var S = session ? sessionStorage : localStorage;
        var data = S.getItem(sname);
        return data ? JSON.parse(data).data : undefined;
    }
    PublicTool.prototype.delStorage = function(sname, session){
        var S = session ? sessionStorage : localStorage;
        S.removeItem(sname);
    }

    /** 其他操作类
     */
    //获取url参数
    PublicTool.prototype.getParamUrl = function(name){
        if (!name) return undefined;
        const params = new URLSearchParams(window.location.search);
        return params.get(name);
    }
    /**
     * 相同时间有仅只有一个相同函数方法运行
     * 类似于函数节流，优势：不受时间限制，直到函数执行完成，适用于异步操作
     * 也可以在指定时间段内， 不允许重复执行相同函数。类似于防抖，立即执行
     * @param {function} fn 执行函数
     * @param {number} time 允许再次执行的间隔时间（ms）
     */
    PublicTool.prototype.noRepeatFn = function(fn, time) {
        let run = false;
        return async () => {
            if(run) {
                console.error('上一次相同的函数方法未执行完成.')
                return;
            }
            run = true;
            try{
                await fn();
            }catch(e){
                console.error(e);
            } finally{
                run = false;
            }
            // 指定时间段内
            if(time && time > 0){
                setTimeout(()=>{
                    run = false
                }, time)
            }
        }
    }
    /**
     * 创建时间段的微任务，配合async阻断代码执行
     */
    PublicTool.prototype.awaitTimeout = function(time){
        return new Promise(reslove => {
            setTimeout(reslove, time)
        })
    }
    /**
     * 轮询函数
     * @param {function} fn 轮询时运行的函数
     * @param {number} [time] 可选，轮询时间间隔
     * @param {boolean} [immediate] 可选，是否立即运行fn函数
     * @returns { stop方法，用于停止轮询 }
     */
    PublicTool.prototype.LoopFn = function (fn, time = 1000, immediate = true){
        this.timeout = null
        this.run = (fn) => {
            this.timeout = setTimeout(()=>{
            fn()
            this.run(fn)
            }, time)
        }
        this.stop = () => {
            clearTimeout(this.timeout)
            this.timeout = undefined
        }
        //首次立即运行
        if(immediate) fn()
        this.run(fn)
        return {
            // 结束轮询
            stop: ()=> {
                this.stop()
            }
        }
    }
    //排序函数
    PublicTool.prototype.sort = function(a, b){
        if(this.isNumber(a) && this.isNumber(b)) return a - b;
        if(this.isString(a) && this.isString(b)) return a.localeCompare(b);
    }
    //去除前后空格
    PublicTool.prototype.trim = function(str){ return str.replace(/(^\s*)|(\s*$)/g, ""); }
    //去除前空格
    PublicTool.prototype.trimLeft = function(str){ return str.replace(/(^\s*)/g, ""); }
    //去除后空格
    PublicTool.prototype.trimRight = function(str){ return str.replace(/(\s*$)/g, ""); }
    //生成随机id
    PublicTool.prototype.getUUID = function(){
        var str=[];
        var Chars='0123456789abcdefghijklmnopqrstuvwxyz';
        for(var i=0;i<36;i++) str[i]=Chars.substr(Math.floor(Math.random()*16),1);
        str[0]=str[8]=str[13]=str[18]=str[23]='-';
        return "tp"+ str.join("")
    }
    //添加收藏夹
    PublicTool.prototype.addFavorite = function(sURL, sTitle){
        try {
            window.external.addFavorite(sURL, sTitle);
        } catch (e) {
            try {
                window.sidebar.addPanel(sTitle, sURL, "");
            } catch (e) {
                console.warn("加入收藏失败，请使用Ctrl+D进行添加");
            }
        }
    }
    //开启全屏
    PublicTool.prototype.toFullScreen = function(){
        let elem = document.body;
        elem.webkitRequestFullScreen
            ? elem.webkitRequestFullScreen() : elem.mozRequestFullScreen
            ? elem.mozRequestFullScreen() : elem.msRequestFullscreen
            ? elem.msRequestFullscreen() : elem.requestFullScreen
            ? elem.requestFullScreen() : console.error('浏览器不支持全屏');
    }
    //退出全屏
    PublicTool.prototype.exitFullscreen = function(){
        let elem = parent.document;
        elem.webkitCancelFullScreen
            ? elem.webkitCancelFullScreen() : elem.mozCancelFullScreen
            ? elem.mozCancelFullScreen() : elem.cancelFullScreen
            ? elem.cancelFullScreen() : elem.msExitFullscreen
            ? elem.msExitFullscreen() : elem.exitFullscreen
            ? elem.exitFullscreen() : console.error('浏览器不支持');
    }
    /**
     * 防抖函数
     * @param fn 防抖执行函数
     * @param delay 防抖一次时间
     * @param immdiate 是否立即执行
     * @param resultCallback 防抖执行函数执行结果的回调函数，参数是防抖执行函数的结果
     * @returns function，可调用cancel方法取消防抖执行
     */
    PublicTool.prototype.debounce = function(
        fn,
        delay = 200,
        immdiate = false,
        resultCallback
    ) {
        let timer = null;
        let isInvoke = false;
        function _debounce(...arg) {
            if (timer) clearTimeout(timer);
            if (immdiate && !isInvoke) {
            const result = fn.apply(this, arg);
            if (resultCallback && typeof resultCallback === "function")
                resultCallback(result);
            isInvoke = true;
            } else {
            timer = setTimeout(() => {
                const result = fn.apply(this, arg);
                if (resultCallback && typeof resultCallback === "function")
                resultCallback(result);
                isInvoke = false;
                timer = null;
            }, delay);
            }
        }

        _debounce.cancel = function () {
            if (timer) clearTimeout(timer);
            timer = null;
            isInvoke = false;
        };

        return _debounce;
    }
    //节流函数(返回一个新函数。立即执行，指定时间内不会再次执行)
    PublicTool.prototype.throttle = function(func, delay){
        let timer, last;
        return function () {
            const _this = this;
            const _args = arguments;
            const now = +new Date();
            if (last && now < last + delay) {
                clearTimeout(timer);
                timer = setTimeout(() => {
                    last = now;
                    func.apply(_this, _args);
                }, delay);
            } else {
                last = now;
                func.apply(this, _args);
            }
        }
    }
    //表单序列化(返回一个表单数据对象。 若表单不存在，返回空对象)
    PublicTool.prototype.serializeJson = function(formId){
        var parent = document.getElementById(formId);
        var inputs = [];
        if(parent){
            var inputs = parent.querySelectorAll("input,textarea,select") || [];
        }
        var obj = {}, name = "";
        for(var i = 0; i < inputs.length; i++ ){
            name = inputs[i].name || inputs[i].tagName+ "_"+ i;
            obj[name] = inputs[i].value;
        }
        return obj;
    }

    /** 脚本读取
     */
    PublicTool.prototype.loadScript = function(src){
        return new Promise(function(resolve, reject) {
            let script = document.createElement('script');
            script.src = src;

            script.onload = () => resolve(script);
            script.onerror = () => reject(new Error(`Script load error for ${src}`));

            document.body.appendChild(script);
        });
    }
    PublicTool.prototype.loadCss = function(src){
        return new Promise(function(resolve, reject) {
            let link = document.createElement('link');
            link.href = url;
            link.type = "text/css";
            link.rel = "stylesheet";

            link.onload = () => resolve(script);
            link.onerror = () => reject(new Error(`CSS load error for ${src}`));

            document.head.appendChild(link);
        });
    }
    PublicTool.prototype.downRunScript = function(url, callback){
        const ajax = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")
        ajax.open("get",url,true)
        ajax.onreadystatechange = function() {
            if (ajax.readyState === 4) {
                if (ajax.status === 200) {
                    new Function(ajax.responseText)()
                    typeof callback === 'function' && callback()
                }
            }
        }
        ajax.send()
    }

    /** 统一各浏览器toFixed表现不一的处理函数
     * number: 数字
     * m: 保留小数位
     */ 
    PublicTool.prototype.toFixed = function(number, m) {
        var value = number
        var bit = m
        var pow = Math.pow(10, bit)
        var arr = (value*pow).toString().split(".")
        var result = ""
        if(arr[1] && arr[1].split('')[0] > 4){
            result = (Number(arr[0]) + 1) / pow
        }else{
            result = arr[0] / pow
        }
        result = result.toString()

        if(bit <= 0) return result;

        if(result.indexOf('.') < 0) result += '.'
        var hasBit = result.split('.')[1] ? result.split('.')[1].length : 0;
        return result + new Array(bit - hasBit + 1).join('0')
    }

    /**
     * @description 小数位处理
     * @param value 即将要处理的值
     * @param exp 最多小数位
     * @param isRound 是否四舍五入, 默认false，直接截取
     * @returns 一个处理后的小数位数字
     */
    PublicTool.prototype.floor = function (value, exp, isRound = false) {
        try {
            if (exp === undefined || exp === null) return value;
            const val = value.toString();
            let result: string | number = val;
            // 四舍五入
            if (isRound) result = Number(val).toFixed(exp);
            // 截取
            else {
            const dotIndex = val.search(/\./);
            result = dotIndex ? val.substring(0, dotIndex + exp + 1) : value;
            }
            return Number(result);
        } catch (e) {
            console.error(e);
            return value;
        }
    };

    /** 四则运算操作(加减乘除)
     * arg: 运算的第二个数
     * acc: 保留小数位
     * 例如："2".add(4); 、(2.77).add(3.33, 1);
     */
    PublicTool.prototype.Add = function(arg, acc){
        var r1, r2, m;
        try {
            r1 = this.toString().split('.')[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg.toString().split('.')[1].length;
        } catch (e) {
            r2 = 0;
        }
        m = Math.pow(10, Math.max(r1, r2));
        var a = parseInt(this * m + 0.5);
        // 兼容减法
        var b = arg < 0 ? parseInt(arg * m) : parseInt(arg * m + 0.5);
        var result = (a + b) / m;
        // console.log('add', arg, arg * m, a, b, m);
        // 小数位数处理
        if (acc) {
            return Number(Number(result).toFixed(parseInt(acc)));
        } else {
            return result;
        }
    }
    PublicTool.prototype.Sub = function(arg, acc){
        return this.add(-arg, acc);
    }
    PublicTool.prototype.Mul = function(arg, acc){
        var m = 0;
        var s1 = this.toString();
        var s2 = arg.toString();
        try {
            m += s1.split('.')[1].length;
        } catch (e) {
            // console.log('catch mul', e);
        }
        try {
            m += s2.split('.')[1].length; // m是累加结果
        } catch (e) {}
        var result =
            (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) /
            Math.pow(10, m);
        // 小数位数处理
        if (acc) {
            return Number(Number(result).toFixed(parseInt(acc)));
        } else {
            return result;
        }
    }
    PublicTool.prototype.Div = function(arg, acc){
        var r1, r2, m;
        try {
            r1 = this.toString().split('.')[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg.toString().split('.')[1].length;
        } catch (e) {
            r2 = 0;
        }
        // 解决分子分母含有0的情况
        if (!this || !arg) {
            return 0;
        }

        m = Math.pow(10, Math.max(r1, r2));
        var a = parseInt(this * m + 0.5);
        var b = parseInt(arg * m + 0.5);
        var result = a / b;
        // 小数位数处理
        if (acc) {
            return Number(Number(result).toFixed(parseInt(acc)));
        } else {
            return result;
        }
    }
    Number.prototype.add = String.prototype.add = PublicTool.prototype.Add;
    Number.prototype.sub = String.prototype.sub = PublicTool.prototype.Sub;
    Number.prototype.mul = String.prototype.mul = PublicTool.prototype.Mul;
    Number.prototype.div = String.prototype.div = PublicTool.prototype.Div;

    // 操作系统、系统版本
    const system = {
        UA: navigator.userAgent,
        isIos: function () {
            return this.UA.includes('iPhone') || this.UA.includes('iPad');
        },
        isAndroid: function () {
            return this.UA.includes('Android');
        },
        getVersion: function () {
            if (this.isIos()) {
                const ver = this.UA.toLowerCase().match(/cpu iphone os (.*?) like mac os/);
                return ver[1].replace(/_/g, '.');
            } else if (this.isAndroid()) {
                const ver = this.UA.toLowerCase().match(/(android.*?(?=;)|micromessenger.*?(?= ))/);
                return ver[0].replace(/[^0-9.]/g, '');
            }
        }
    };


    window.TP = new PublicTool();
    window.system = system;

})();

